/*	SCCS Id: @(#)winami.c	3.2	2000/01/12	*/
/* Copyright (c) Gregg Wonderly, Naperville, Illinois,  1991,1992,1993,1996. */
/* NetHack may be freely redistributed.  See license for details. */

#include "NH:sys/amiga/windefs.h"
#include "NH:sys/amiga/winext.h"
#include "NH:sys/amiga/winproto.h"
#include "dlb.h"

#ifdef AMIGA_INTUITION

static int FDECL( put_ext_cmd, ( char *, int, struct amii_WinDesc *, int ) );

struct amii_DisplayDesc *amiIDisplay;	/* the Amiga Intuition descriptor */
struct Rectangle lastinvent, lastmsg;
int clipping = 0;
int clipx=0;
int clipy=0;
int clipxmax=0;
int clipymax=0;
int scrollmsg = 1;
int alwaysinvent = 0;
int amii_numcolors;
long amii_scrnmode;

/* Interface definition, for use by windows.c and winprocs.h to provide
 * the intuition interface for the amiga...
 */
struct window_procs amii_procs =
{
    "amii",
    WC_COLOR|WC_HILITE_PET|WC_INVERSE,
    amii_init_nhwindows,
    amii_player_selection,
    amii_askname,
    amii_get_nh_event,
    amii_exit_nhwindows,
    amii_suspend_nhwindows,
    amii_resume_nhwindows,
    amii_create_nhwindow,
    amii_clear_nhwindow,
    amii_display_nhwindow,
    amii_destroy_nhwindow,
    amii_curs,
    amii_putstr,
    amii_display_file,
    amii_start_menu,
    amii_add_menu,
    amii_end_menu,
    amii_select_menu,
    genl_message_menu,
    amii_update_inventory,
    amii_mark_synch,
    amii_wait_synch,
#ifdef CLIPPING
    amii_cliparound,
#endif
#ifdef POSITIONBAR
    donull,
#endif
    amii_print_glyph,
    amii_raw_print,
    amii_raw_print_bold,
    amii_nhgetch,
    amii_nh_poskey,
    amii_bell,
    amii_doprev_message,
    amii_yn_function,
    amii_getlin,
    amii_get_ext_cmd,
    amii_number_pad,
    amii_delay_output,
#ifdef CHANGE_COLOR	/* only a Mac option currently */
    amii_change_color,
    amii_get_color_string,
#endif
    /* other defs that really should go away (they're tty specific) */
    amii_delay_output,
    amii_delay_output,
    amii_outrip,
    genl_preference_update
};

/* The view window layout uses the same function names so we can use
 * a shared library to allow the executable to be smaller.
 */
struct window_procs amiv_procs =
{
    "amitile",
    WC_COLOR|WC_HILITE_PET|WC_INVERSE,
    amii_init_nhwindows,
    amii_player_selection,
    amii_askname,
    amii_get_nh_event,
    amii_exit_nhwindows,
    amii_suspend_nhwindows,
    amii_resume_nhwindows,
    amii_create_nhwindow,
    amii_clear_nhwindow,
    amii_display_nhwindow,
    amii_destroy_nhwindow,
    amii_curs,
    amii_putstr,
    amii_display_file,
    amii_start_menu,
    amii_add_menu,
    amii_end_menu,
    amii_select_menu,
    genl_message_menu,
    amii_update_inventory,
    amii_mark_synch,
    amii_wait_synch,
#ifdef CLIPPING
    amii_cliparound,
#endif
#ifdef POSITIONBAR
    donull,
#endif
    amii_print_glyph,
    amii_raw_print,
    amii_raw_print_bold,
    amii_nhgetch,
    amii_nh_poskey,
    amii_bell,
    amii_doprev_message,
    amii_yn_function,
    amii_getlin,
    amii_get_ext_cmd,
    amii_number_pad,
    amii_delay_output,
#ifdef CHANGE_COLOR	/* only a Mac option currently */
    amii_change_color,
    amii_get_color_string,
#endif
    /* other defs that really should go away (they're tty specific) */
    amii_delay_output,
    amii_delay_output,
    amii_outrip,
    genl_preference_update
};

unsigned short amii_initmap[ AMII_MAXCOLORS ];
/* Default pens used unless user overides in nethack.cnf. */
unsigned short amii_init_map[ AMII_MAXCOLORS ] =
{
    0x0000, /* color #0  C_BLACK    */
    0x0FFF, /* color #1  C_WHITE    */
    0x0830, /* color #2  C_BROWN    */
    0x07ac, /* color #3  C_CYAN     */
    0x0181, /* color #4  C_GREEN    */
    0x0C06, /* color #5  C_MAGENTA  */
    0x023E, /* color #6  C_BLUE     */
    0x0c00, /* color #7  C_RED      */
};

unsigned short amiv_init_map[ AMII_MAXCOLORS ] =
{
    0x0000, /* color #0  C_BLACK    */
    0x0fff, /* color #1  C_WHITE    */
    0x00bf, /* color #2  C_CYAN     */
    0x0f60, /* color #3  C_ORANGE   */
    0x000f, /* color #4  C_BLUE     */
    0x0090, /* color #5  C_GREEN    */
    0x069b, /* color #6  C_GREY     */
    0x0f00, /* color #7  C_RED      */
    0x06f0, /* color #8  C_LTGREEN  */
    0x0ff0, /* color #9  C_YELLOW   */
    0x0f0f, /* color #10 C_MAGENTA  */
    0x0940, /* color #11 C_BROWN    */
    0x0466, /* color #12 C_GREYBLUE */
    0x0c40, /* color #13 C_LTBROWN  */
    0x0ddb, /* color #14 C_LTGREY   */
    0x0fb9, /* color #15 C_PEACH    */

    /* Pens for dripens etc under AA or better */
    0x0222, /* color #16 */
    0x0fdc, /* color #17 */
    0x0000, /* color #18 */
    0x0ccc, /* color #19 */
    0x0bbb, /* color #20 */
    0x0BA9, /* color #21 */
    0x0999, /* color #22 */
    0x0987, /* color #23 */
    0x0765, /* color #24 */
    0x0666, /* color #25 */
    0x0555, /* color #26 */
    0x0533, /* color #27 */
    0x0333, /* color #28 */
    0x018f, /* color #29 */
    0x0f81, /* color #30 */
    0x0fff, /* color #31 */
};

#if !defined( TTY_GRAPHICS ) || defined( SHAREDLIB )	/* this should be shared better */
char morc;  /* the character typed in response to a --more-- prompt */
#endif
char spaces[ 76 ] =
"                                                                           ";

winid WIN_BASE = WIN_ERR;
winid WIN_OVER = WIN_ERR;
winid amii_rawprwin = WIN_ERR;

/* Changed later during window/screen opens... */
int txwidth = FONTWIDTH, txheight = FONTHEIGHT, txbaseline = FONTBASELINE;

/* If a 240 or more row screen is in front when we start, this will be
 * set to 1, and the windows will be given borders to allow them to be
 * arranged differently.  The Message window may eventually get a scroller...
 */
int bigscreen = 0;

/* This gadget data is replicated for menu/text windows... */
struct PropInfo PropScroll = { AUTOKNOB|FREEVERT,
					0xffff,0xffff, 0xffff,0xffff, };
struct Image Image1 = { 0,0, 7,102, 0, NULL, 0x0000,0x0000, NULL };
struct Gadget MenuScroll = {
    NULL, -15,10, 15,-19, GRELRIGHT|GRELHEIGHT,
    RELVERIFY|FOLLOWMOUSE|RIGHTBORDER|GADGIMMEDIATE|RELVERIFY,
    PROPGADGET, (APTR)&Image1, NULL, NULL, NULL, (APTR)&PropScroll,
    1, NULL
};

/* This gadget is for the message window... */
struct PropInfo MsgPropScroll = { AUTOKNOB|FREEVERT,
					0xffff,0xffff, 0xffff,0xffff, };
struct Image MsgImage1 = { 0,0, 7,102, 0, NULL, 0x0000,0x0000, NULL };
struct Gadget MsgScroll = {
    NULL, -15,10, 14,-19, GRELRIGHT|GRELHEIGHT,
    RELVERIFY|FOLLOWMOUSE|RIGHTBORDER|GADGIMMEDIATE|RELVERIFY,
    PROPGADGET, (APTR)&MsgImage1, NULL, NULL, NULL, (APTR)&MsgPropScroll,
    1, NULL
};

int wincnt=0;   /* # of nh windows opened */

/* We advertise a public screen to allow some people to do other things
 * while they are playing...  like compiling...
 */

#ifdef  INTUI_NEW_LOOK
extern struct Hook fillhook;
struct TagItem tags[] =
{
    { WA_BackFill, (ULONG)&fillhook },
    { WA_PubScreenName, (ULONG)"NetHack" },
    { TAG_DONE, 0 },
};
#endif

/*
 * The default dimensions and status values for each window type.  The
 * data here is generally changed in create_nhwindow(), so beware that
 * what you see here may not be exactly what you get.
 */
struct win_setup new_wins[] =
{

    /* First entry not used, types are based at 1 */
    {{0}},

    /* NHW_MESSAGE */
    {{0,1,640,11,
    0xff,0xff,
    NEWSIZE|GADGETUP|GADGETDOWN|MOUSEMOVE|MOUSEBUTTONS|RAWKEY,
    BORDERLESS|ACTIVATE|SMART_REFRESH
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    NULL,NULL,(UBYTE*)"Messages",NULL,NULL,320,40,0xffff,0xffff,
#ifdef  INTUI_NEW_LOOK
    PUBLICSCREEN,tags
#else
    CUSTOMSCREEN
#endif
    },
    0,0,1,1,80,80},

    /* NHW_STATUS */
    {{0,181,640,24,
    0xff,0xff,
    RAWKEY|MENUPICK|DISKINSERTED,
    BORDERLESS|ACTIVATE|SMART_REFRESH|BACKDROP
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    NULL,NULL,(UBYTE*)"Game Status",NULL,NULL,0,0,0xffff,0xffff,
#ifdef  INTUI_NEW_LOOK
    PUBLICSCREEN,tags
#else
    CUSTOMSCREEN
#endif
    },
    0,0,2,2,78,78},

    /* NHW_MAP */
    {{0,0,WIDTH,WINDOWHEIGHT,
    0xff,0xff,
    RAWKEY|MENUPICK|MOUSEBUTTONS|ACTIVEWINDOW|MOUSEMOVE,
    BORDERLESS|ACTIVATE|SMART_REFRESH|BACKDROP
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    NULL,NULL,(UBYTE*)"Dungeon Map",NULL,NULL,64,64,0xffff,0xffff,
#ifdef  INTUI_NEW_LOOK
    PUBLICSCREEN,tags
#else
    CUSTOMSCREEN
#endif
    },
    0,0,22,22,80,80},

    /* NHW_MENU */
    {{400,10,10,10,
    0xff,0xff,
    RAWKEY|MENUPICK|DISKINSERTED|MOUSEMOVE|MOUSEBUTTONS|
    GADGETUP|GADGETDOWN|CLOSEWINDOW|VANILLAKEY|NEWSIZE|INACTIVEWINDOW,
    WINDOWSIZING|WINDOWCLOSE|WINDOWDRAG|ACTIVATE|SMART_REFRESH
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    &MenuScroll,NULL,NULL,NULL,NULL,64,32,0xffff,0xffff,
#ifdef  INTUI_NEW_LOOK
    PUBLICSCREEN,tags
#else
    CUSTOMSCREEN
#endif
    },
    0,0,1,1,22,78},

    /* NHW_TEXT */
    {{0,0,640,200,
    0xff,0xff,
    RAWKEY|MENUPICK|DISKINSERTED|MOUSEMOVE|
    GADGETUP|CLOSEWINDOW|VANILLAKEY|NEWSIZE,
    WINDOWSIZING|WINDOWCLOSE|WINDOWDRAG|ACTIVATE|SMART_REFRESH
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    &MenuScroll,NULL,(UBYTE*)NULL,NULL,NULL,100,32,0xffff,0xffff,
#ifdef  INTUI_NEW_LOOK
    PUBLICSCREEN,tags
#else
    CUSTOMSCREEN
#endif
    },
    0,0,1,1,22,78},

    /* NHW_BASE */
    {{0,0,WIDTH,WINDOWHEIGHT,
    0xff,0xff,
    RAWKEY|MENUPICK|MOUSEBUTTONS,
    BORDERLESS|ACTIVATE|SMART_REFRESH|BACKDROP
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    NULL,NULL,(UBYTE*)NULL,NULL,NULL,-1,-1,0xffff,0xffff,
#ifdef  INTUI_NEW_LOOK
    PUBLICSCREEN,tags
#else
    CUSTOMSCREEN
#endif
    },
    0,0,22,22,80,80},

    /* NHW_OVER */
    {{320,20,319,179,
    0xff,0xff,
    RAWKEY|MENUPICK|MOUSEBUTTONS,
    BORDERLESS|ACTIVATE|SMART_REFRESH|BACKDROP
#ifdef  INTUI_NEW_LOOK
    |WFLG_NW_EXTENDED
#endif
    ,
    NULL,NULL,(UBYTE*)NULL,NULL,NULL,64,32,0xffff,0xffff,
#ifdef  INTUI_NEW_LOOK
    PUBLICSCREEN,tags
#else
    CUSTOMSCREEN
#endif
    },
    0,0,22,22,80,80},
};

const char winpanicstr[] = "Bad winid %d in %s()";

/* The opened windows information */
struct amii_WinDesc *amii_wins[ MAXWIN + 1 ];

#ifdef  INTUI_NEW_LOOK
/*
 * NUMDRIPENS varies based on headers, so don't use it
 * here, its value is used elsewhere.
 */
UWORD amii_defpens[ 20 ];

struct TagItem scrntags[] =
{
    { SA_PubName, (ULONG)"NetHack" },
    { SA_Overscan, OSCAN_TEXT },
    { SA_AutoScroll, TRUE },
#if LIBRARY_VERSION >= 39
    { SA_Interleaved, TRUE },
#endif
    { SA_Pens, (ULONG)0 },
    { SA_DisplayID, 0 },
    { TAG_DONE, 0 },
};

#endif

struct NewScreen NewHackScreen =
{
    0, 0, WIDTH, SCREENHEIGHT, 3,
    0, 1,     /* DetailPen, BlockPen */
    HIRES,
    CUSTOMSCREEN
#ifdef  INTUI_NEW_LOOK
    |NS_EXTENDED
#endif
    ,
    &Hack80,  /* Font */
    NULL,     /*(UBYTE *)" NetHack X.Y.Z" */
    NULL,     /* Gadgets */
    NULL,     /* CustomBitmap */
#ifdef  INTUI_NEW_LOOK
    scrntags
#endif
};

/*
 * plname is filled either by an option (-u Player  or  -uPlayer) or
 * explicitly (by being the wizard) or by askname.
 * It may still contain a suffix denoting pl_character.
 * Always called after init_nhwindows() and before display_gamewindows().
 */
void
amii_askname()
{
    char plnametmp[300]; /* From winreq.c: sizeof(StrStringSIBuff) */
    *plnametmp = 0;
    do {
	amii_getlin( "Who are you?", plnametmp );
    } while( strlen( plnametmp ) == 0 );

    strncpy(plname, plnametmp, PL_NSIZ-1); /* Avoid overflowing plname[] */
    plname[PL_NSIZ-1] = 0;

    if( *plname == '\33' )
    {
	clearlocks();
	exit_nhwindows(NULL);
	terminate(0);
    }
}

/* Discarded ... -jhsa
#include "NH:sys/amiga/char.c"
*/

/* Get the player selection character */

#if 0 /* New function at the bottom */
void
amii_player_selection()
{
    register struct Window *cwin;
    register struct IntuiMessage *imsg;
    register int aredone = 0;
    register struct Gadget *gd;
    static int once = 0;
    long class, code;

    amii_clear_nhwindow( WIN_BASE );
    if (validrole(flags.initrole))
	return;
    else {
	flags.initrole=randrole();
	return;
    }
#if 0 /* Don't query the user ... instead give random character -jhsa */

#if 0	/* OBSOLETE */
    if( *pl_character ){
	pl_character[ 0 ] = toupper( pl_character[ 0 ] );
	if( index( pl_classes, pl_character[ 0 ] ) )
	    return;
    }
#endif

    if( !once ){
	if( bigscreen ){
	    Type_NewWindowStructure1.TopEdge =
	      (HackScreen->Height/2) - (Type_NewWindowStructure1.Height/2);
	}
	for( gd = Type_NewWindowStructure1.FirstGadget; gd;
	  gd = gd->NextGadget )
	{
	    if( gd->GadgetID != 0 )
		SetBorder( gd );
	}
	once = 1;
    }

    if( WINVERS_AMIV )
    {
# ifdef	INTUI_NEW_LOOK
	Type_NewWindowStructure1.Extension = wintags;
	Type_NewWindowStructure1.Flags |= WFLG_NW_EXTENDED;
	fillhook.h_Entry = (ULONG(*)())LayerFillHook;
	fillhook.h_Data = (void *)-2;
	fillhook.h_SubEntry = 0;
#endif
    }

    Type_NewWindowStructure1.Screen = HackScreen;
    if( ( cwin = OpenShWindow( (void *)&Type_NewWindowStructure1 ) ) == NULL )
    {
	return;
    }
#if 0
    WindowToFront( cwin );
#endif

    while( !aredone )
    {
	WaitPort( cwin->UserPort );
	while( ( imsg = (void *) GetMsg( cwin->UserPort ) ) != NULL )
	{
	    class = imsg->Class;
	    code = imsg->Code;
	    gd = (struct Gadget *)imsg->IAddress;
	    ReplyMsg( (struct Message *)imsg );

	    switch( class )
	    {
	    case VANILLAKEY:
		if( index( pl_classes, toupper( code ) ) )
		{
		    pl_character[0] = toupper( code );
		    aredone = 1;
		}
		else if( code == ' ' || code == '\n' || code == '\r' )
		{
		    flags.initrole = randrole();
#if 0	/* OBSOLETE */
#ifdef  TOURIST
		    strcpy( pl_character, roles[ rnd( 11 ) ] );
#else
		    strcpy( pl_character, roles[ rnd( 10 ) ] );
#endif
#endif
		    aredone = 1;
		    amii_clear_nhwindow( WIN_BASE );
		    CloseShWindow( cwin );
		    RandomWindow( pl_character );
		    return;
		}
		else if( code == 'q' || code == 'Q' )
		{
		CloseShWindow( cwin );
		clearlocks();
		exit_nhwindows(NULL);
		terminate(0);
		}
		else
		    DisplayBeep( NULL );
		break;

	    case GADGETUP:
		switch( gd->GadgetID )
		{
		case 1: /* Random Character */
		    flags.initrole = randrole();
#if 0	/* OBSOLETE */
#ifdef  TOURIST
		    strcpy( pl_character, roles[ rnd( 11 ) ] );
#else
		    strcpy( pl_character, roles[ rnd( 10 ) ] );
#endif
#endif
		    amii_clear_nhwindow( WIN_BASE );
		    CloseShWindow( cwin );
		    RandomWindow( pl_character );
		    return;

		default:
		    pl_character[0] = gd->GadgetID;
		    break;
		}
		aredone = 1;
		break;

	    case CLOSEWINDOW:
		CloseShWindow( cwin );
		clearlocks();
		exit_nhwindows(NULL);
		terminate(0);
		break;
	    }
	}
    }
    amii_clear_nhwindow( WIN_BASE );
    CloseShWindow( cwin );
#endif /* Do not query user ... -jhsa */
}
#endif /* Function elsewhere */

#if 0 /* Unused ... -jhsa */

#include "NH:sys/amiga/randwin.c"

void
RandomWindow( name )
    char *name;
{
    struct MsgPort *tport;
    struct timerequest *trq;
    static int once = 0;
    struct Gadget *gd;
    struct Window *w;
    struct IntuiMessage *imsg;
    int ticks = 0, aredone = 0, timerdone = 0;
    long mask, got;

    tport = CreateMsgPort();
    trq = (struct timerequest *)CreateIORequest( tport, sizeof( *trq ) );
    if( tport == NULL || trq == NULL )
    {
allocerr:
	if( tport ) DeleteMsgPort( tport );
	if( trq ) DeleteIORequest( (struct IORequest *)trq );
	Delay( 8 * 50 );
	return;
    }

    if( OpenDevice( TIMERNAME, UNIT_VBLANK, (struct IORequest *)trq, 0L ) != 0 )
	goto allocerr;

    trq->tr_node.io_Command = TR_ADDREQUEST;
    trq->tr_time.tv_secs = 8;
    trq->tr_time.tv_micro = 0;

    SendIO( (struct IORequest *)trq );

    /* Place the name in the center of the screen */
    Rnd_IText5.IText = name;
    Rnd_IText6.LeftEdge = Rnd_IText4.LeftEdge +
		(strlen(Rnd_IText4.IText)+1)*8;
    Rnd_NewWindowStructure1.Width = (
	    (strlen( Rnd_IText2.IText )+1) * 8 ) +
	    HackScreen->WBorLeft + HackScreen->WBorRight;
    Rnd_IText5.LeftEdge = (Rnd_NewWindowStructure1.Width -
	    (strlen(name)*8))/2;

    gd = Rnd_NewWindowStructure1.FirstGadget;
    gd->LeftEdge = (Rnd_NewWindowStructure1.Width - gd->Width)/2;
	/* Chose correct modifier */
    Rnd_IText6.IText = "a";
    switch( *name )
    {
    case 'a': case 'e': case 'i': case 'o':
    case 'u': case 'A': case 'E': case 'I':
    case 'O': case 'U':
	Rnd_IText6.IText = "an";
	break;
    }

    if( !once )
    {
	if( bigscreen )
	{
	    Rnd_NewWindowStructure1.TopEdge =
		(HackScreen->Height/2) - (Rnd_NewWindowStructure1.Height/2);
	}
	for( gd = Rnd_NewWindowStructure1.FirstGadget; gd; gd = gd->NextGadget )
	{
	    if( gd->GadgetID != 0 )
		SetBorder( gd );
	}
	Rnd_NewWindowStructure1.IDCMPFlags |= VANILLAKEY;

	once = 1;
    }

    if( WINVERS_AMIV )
    {
#ifdef	INTUI_NEW_LOOK
	Rnd_NewWindowStructure1.Extension = wintags;
	Rnd_NewWindowStructure1.Flags |= WFLG_NW_EXTENDED;
	fillhook.h_Entry = (ULONG(*)())LayerFillHook;
	fillhook.h_Data = (void *)-2;
	fillhook.h_SubEntry = 0;
#endif
    }

    Rnd_NewWindowStructure1.Screen = HackScreen;
    if( ( w = OpenShWindow( (void *)&Rnd_NewWindowStructure1 ) ) == NULL )
    {
	AbortIO( (struct IORequest *)trq );
	WaitIO( (struct IORequest *)trq );
	CloseDevice( (struct IORequest *)trq );
	DeleteIORequest( (struct IORequest *) trq );
	DeleteMsgPort( tport );
	Delay( 50 * 8 );
	return;
    }

    PrintIText( w->RPort, &Rnd_IntuiTextList1, 0, 0 );

    mask = (1L << tport->mp_SigBit)|(1L << w->UserPort->mp_SigBit);
    while( !aredone )
    {
	got = Wait( mask );
	if( got & (1L << tport->mp_SigBit ) )
	{
	    aredone = 1;
	    timerdone = 1;
	    GetMsg( tport );
        }
        while( w && ( imsg = (struct IntuiMessage *) GetMsg( w->UserPort ) ) )
        {
	    switch( (long)imsg->Class )
	    {
		/* Must be up for a little while... */
	    case INACTIVEWINDOW:
		if( ticks >= 40 )
		    aredone = 1;
		break;

	    case INTUITICKS:
		++ticks;
		break;

	    case GADGETUP:
		aredone = 1;
		break;

	    case VANILLAKEY:
		if(imsg->Code=='\n' || imsg->Code==' ' || imsg->Code=='\r')
		    aredone = 1;
		break;
	    }
	    ReplyMsg( (struct Message *)imsg );
        }
    }

    if( !timerdone )
    {
	AbortIO( (struct IORequest *)trq );
	WaitIO( (struct IORequest *)trq );
    }

    CloseDevice( (struct IORequest *)trq );
    DeleteIORequest( (struct IORequest *) trq );
    DeleteMsgPort( tport );
    if(w) CloseShWindow( w );
}
#endif /* Discarded randwin ... -jhsa */

/* this should probably not be needed (or be renamed)
void
flush_output(){} */

/* Read in an extended command - doing command line completion for
 * when enough characters have been entered to make a unique command.
 */
int
amii_get_ext_cmd( void )
{
    menu_item *mip;
    anything id;
    struct amii_WinDesc *cw;
#ifdef EXTMENU
    winid win;
    int i;
    char buf[256];
#endif
    int colx;
    int bottom = 0;
    struct Window *w;
    char obufp[ 100 ];
    register char *bufp = obufp;
    register int c;
    int com_index, oindex;
    int did_comp=0;	/* did successful completion? */
    int sel = -1;

    if( WIN_MESSAGE == WIN_ERR || ( cw = amii_wins[ WIN_MESSAGE ] ) == NULL )
	panic(winpanicstr, WIN_MESSAGE, "get_ext_cmd");
    w = cw->win;
    bottom = amii_msgborder( w );
    colx = 3;

#ifdef	EXTMENU
    if (iflags.extmenu) {
    win = amii_create_nhwindow( NHW_MENU );
    amii_start_menu( win );
    pline("#");
    amii_putstr( WIN_MESSAGE, -1, " " );

    for( i = 0; extcmdlist[ i ].ef_txt != NULL; ++i )
    {
	id.a_char = *extcmdlist[ i ].ef_txt;
	sprintf( buf, "%-10s - %s ",
		 extcmdlist[ i ].ef_txt,
		 extcmdlist[ i ].ef_desc );
	amii_add_menu( win, NO_GLYPH, &id, extcmdlist[i].ef_txt[0], 0, 0, buf, MENU_UNSELECTED);
    }

    amii_end_menu( win, (char*)0 );
    sel = amii_select_menu( win, PICK_ONE, &mip );
    amii_destroy_nhwindow( win );

    if( sel == 1 )
    {
	sel = mip->item.a_char;
	for( i = 0; extcmdlist[ i ].ef_txt != NULL; ++i )
	{
	    if( sel == extcmdlist[i].ef_txt[0] )
		break;
	}

	/* copy in the text */
	if( extcmdlist[ i ].ef_txt != NULL )
	{
	    amii_clear_nhwindow( WIN_MESSAGE );
	    (void) put_ext_cmd( (char *)extcmdlist[i].ef_txt, 0, cw, bottom );
	    return( i );
	}
	else
	    DisplayBeep( NULL );
    }

    return( -1 );
    } else {
#else

    amii_clear_nhwindow( WIN_MESSAGE ); /* Was NHW_MESSAGE */
    if( scrollmsg )
    {
	pline("#");
	amii_addtopl(" ");
    }
    else
    {
	pline("# ");
    }

    sel = -1;
    while((c = WindowGetchar()) != EOF)
    {
	amii_curs( WIN_MESSAGE, colx, bottom );
	if(c == '?' )
	{
	    int win, i;
	    char buf[ 100 ];

	    if(did_comp){
		while(bufp!=obufp)
		{
		    bufp--;
		    amii_curs(WIN_MESSAGE, --colx, bottom);
		    Text(w->RPort,spaces,1);
		    amii_curs(WIN_MESSAGE,colx,bottom);
		    did_comp=0;
		}
	    }

	    win = amii_create_nhwindow( NHW_MENU );
	    amii_start_menu( win );

	    for( i = 0; extcmdlist[ i ].ef_txt != NULL; ++i )
	    {
		id.a_char = extcmdlist[i].ef_txt[0];
		sprintf( buf, "%-10s - %s ",
			 extcmdlist[ i ].ef_txt,
			 extcmdlist[ i ].ef_desc );
		amii_add_menu( win, NO_GLYPH, &id, extcmdlist[i].ef_txt[0], 0,
		   0, buf, MENU_UNSELECTED);
	    }

	    amii_end_menu( win, (char*)0 );
	    sel = amii_select_menu( win, PICK_ONE, &mip );
	    amii_destroy_nhwindow( win );

	    if( sel == 0 )
	    {
		return( -1 );
	    }
	    else
	    {
		sel = mip->item.a_char;
		for( i = 0; extcmdlist[ i ].ef_txt != NULL; ++i )
		{
		    if( sel == extcmdlist[i].ef_txt[0] )
			break;
		}

		/* copy in the text */
		if( extcmdlist[ i ].ef_txt != NULL )
		{
		    amii_clear_nhwindow( WIN_MESSAGE );
		    strcpy( bufp = obufp, extcmdlist[ i ].ef_txt );
		    (void) put_ext_cmd( obufp, colx, cw, bottom );
		    return( i );
		}
		else
		    DisplayBeep( NULL );
	    }
	}
	else if(c == '\033')
	{
	    return( -1 );
	}
	else if(c == '\b')
	{
	    if(did_comp){
		while(bufp!=obufp){
		    bufp--;
		    amii_curs(WIN_MESSAGE, --colx, bottom);
		    Text(w->RPort,spaces,1);
		    amii_curs(WIN_MESSAGE,colx,bottom);
		    did_comp=0;
		    sel = -1;
		}
	    }
	    else if(bufp != obufp)
	    {
		sel = -1;
		bufp--;
		amii_curs( WIN_MESSAGE, --colx, bottom);
		Text( w->RPort, spaces, 1 );
		amii_curs( WIN_MESSAGE, colx, bottom);
	    }
	    else
		DisplayBeep( NULL );
	}
	else if( c == '\n' || c == '\r' )
	{
	    return(sel);
	}
	else if( c >= ' ' && c < '\177')
	{
		/* avoid isprint() - some people don't have it
		   ' ' is not always a printing char */
	    *bufp = c;
	    bufp[1] = 0;
	    oindex = 0;
	    com_index = -1;

	    while(extcmdlist[oindex].ef_txt != NULL)
	    {
		if(!strnicmp(obufp, (char *)extcmdlist[oindex].ef_txt, strlen(obufp)))
		{
		    if(com_index == -1) /* No matches yet*/
			com_index = oindex;
		    else /* More than 1 match */
			com_index = -2;
		}
		oindex++;
	    }

	    if(com_index >= 0 && *obufp )
	    {
		Strcpy(obufp, extcmdlist[com_index].ef_txt);
		/* finish printing our string */
		colx = put_ext_cmd( obufp, colx, cw, bottom );
		bufp = obufp; /* reset it */
		if(strlen(obufp) < BUFSZ-1 && strlen(obufp) < COLNO)
		    bufp += strlen(obufp);
		did_comp=1;
		sel = com_index;
	    }
	    else
	    {
		colx = put_ext_cmd( obufp, colx, cw, bottom );
		if(bufp-obufp < BUFSZ-1 && bufp-obufp < COLNO)
		    bufp++;
	    }
	}
	else if(c == ('X'-64) || c == '\177')
	{
	    colx = 0;
	    amii_clear_nhwindow( WIN_MESSAGE );
	    pline( "# " );
	    bufp = obufp;
	} else
	    DisplayBeep( NULL );
    }
    return(-1);
#endif
}

static int
put_ext_cmd( obufp, colx, cw, bottom )
    char *obufp;
    int colx, bottom;
    struct amii_WinDesc *cw;
{
    struct Window *w = cw->win;
    char *t;

    t = (char *)alloc( strlen( obufp ) + 7 );
    if( t != NULL )
    {
	if( scrollmsg )
	{
	    sprintf( t, "xxx%s", obufp );
	    t[0] = 1;
	    t[1] = 1;
	    t[2] = '#';
	    amii_curs( WIN_MESSAGE, 0, bottom);
	    SetAPen( w->RPort, C_WHITE );
	    Text(w->RPort, "># ", 3 );
	    /* SetAPen( w->RPort, C_BLACK ); */ /* Black text on black screen doesn't look too well ... -jhsa */
	    Text(w->RPort, t+3, strlen( t ) - 3 );
	}
	else
	{
	    sprintf( t, "# %s", obufp );
	    amii_curs( WIN_MESSAGE, 0, bottom);
	    SetAPen( w->RPort, C_WHITE );
	    Text(w->RPort, t, strlen( t ) );
	}
	if( scrollmsg )
	    SetAPen( w->RPort, C_WHITE );
	if( cw->data[ cw->maxrow - 1 ] )
	    free( cw->data[ cw->maxrow - 1 ] );
	cw->data[ cw->maxrow - 1 ] = t;
    }
    else
    {
	amii_curs( WIN_MESSAGE, 0, bottom);
	SetAPen( w->RPort, C_WHITE );
	Text(w->RPort, "# ", 2 );
	/* SetAPen( w->RPort, C_BLACK ); */ /* Black on black ... -jhsa */
	Text(w->RPort, obufp, strlen( obufp ) );
	SetAPen( w->RPort, C_WHITE );
    }
    amii_curs( WIN_MESSAGE, colx = strlen( obufp ) + 3 + ( scrollmsg != 0 ), bottom);
    return( colx );
}

/* Ask a question and get a response */

char amii_yn_function(query, resp, def)
const char *query,*resp;
char def;
/*
 *   Generic yes/no function. 'def' is the default (returned by space or
 *   return; 'esc' returns 'q', or 'n', or the default, depending on
 *   what's in the string. The 'query' string is printed before the user
 *   is asked about the string.
 *   If resp is NULL, any single character is accepted and returned.
 *   If not-NULL, only characters in it are allowed (exceptions:  the
 *   quitchars are always allowed, and if it contains '#' then digits
 *   are allowed); if it includes an <esc>, anything beyond that won't
 *   be shown in the prompt to the user but will be acceptable as input.
 */
{
	register char q;
	char rtmp[40];
	boolean digit_ok, allow_num;
	char prompt[QBUFSZ];
	register struct amii_WinDesc *cw;

	if( cw = amii_wins[ WIN_MESSAGE ] )
	    cw->disprows = 0;
	if (resp) {
	    char *rb, respbuf[QBUFSZ];

  	    allow_num = (index(resp, '#') != 0);
	    Strcpy(respbuf, resp);
	    /* any acceptable responses that follow <esc> aren't displayed */
	    if ((rb = index(respbuf, '\033')) != 0) *rb = '\0';
	    Sprintf(prompt, "%s [%s] ", query, respbuf);
	    if (def) Sprintf(eos(prompt), "(%c) ", def);
  	    pline("%s", prompt);
	} else {
	    amii_putstr(WIN_MESSAGE, 0, query);
	    cursor_on(WIN_MESSAGE);
	    q = WindowGetchar();
	    cursor_off(WIN_MESSAGE);
	    *rtmp = q;
	    rtmp[ 1 ] = 0;
	    amii_addtopl(rtmp);
	    goto clean_up;
	}

	do {	/* loop until we get valid input */
	    cursor_on(WIN_MESSAGE);
	    q = lowc(WindowGetchar());
	    cursor_off(WIN_MESSAGE);
#if 0
/* fix for PL2 */
	    if (q == '\020') { /* ctrl-P */
		if(!doprev) (void) tty_doprev_message(); /* need two initially */
		(void) tty_doprev_message();
		q = (char)0;
		doprev = 1;
		continue;
	    } else if(doprev) {
		tty_clear_nhwindow(WIN_MESSAGE);
		cw->maxcol = cw->maxrow;
		doprev = 0;
		amii_addtopl(prompt);
		continue;
	    }
#endif
	    digit_ok = allow_num && isdigit(q);
	    if (q == '\033') {
		if (index(resp, 'q'))
		    q = 'q';
		else if (index(resp, 'n'))
		    q = 'n';
		else
		    q = def;
		break;
	    } else if (index(quitchars, q)) {
		q = def;
		break;
	    }
	    if (!index(resp, q) && !digit_ok) {
		amii_bell();
		q = (char)0;
	    } else if (q == '#' || digit_ok) {
		char z, digit_string[2];
		int n_len = 0;
		long value = 0;
		amii_addtopl("#"),  n_len++;
		digit_string[1] = '\0';
		if (q != '#') {
		    digit_string[0] = q;
		    amii_addtopl(digit_string),  n_len++;
		    value = q - '0';
		    q = '#';
		}
		do {	/* loop until we get a non-digit */
		    cursor_on(WIN_MESSAGE);
		    z = lowc(WindowGetchar());
		    cursor_off(WIN_MESSAGE);
		    if (isdigit(z)) {
			value = (10 * value) + (z - '0');
			if (value < 0) break;	/* overflow: try again */
			digit_string[0] = z;
			amii_addtopl(digit_string),  n_len++;
		    } else if (z == 'y' || index(quitchars, z)) {
			if (z == '\033')  value = -1;	/* abort */
			z = '\n';	/* break */
		    } else if ( z == '\b') {
			if (n_len <= 1) { value = -1;  break; }
			else { value /= 10;  removetopl(1),  n_len--; }
		    } else {
			value = -1;	/* abort */
			amii_bell();
			break;
		    }
		} while (z != '\n');
		if (value > 0) yn_number = value;
		else if (value == 0) q = 'n';		/* 0 => "no" */
		else {	/* remove number from top line, then try again */
			removetopl(n_len),  n_len = 0;
			q = '\0';
		}
	    }
	} while(!q);

	if (q != '#' && q != '\033') {
	    Sprintf(rtmp, "%c", q);
	    amii_addtopl(rtmp);
	}
    clean_up:
	cursor_off(WIN_MESSAGE);
	clear_nhwindow(WIN_MESSAGE);
	return q;
}

void
amii_display_file(fn, complain)
const char *fn;
boolean complain;
{
    register struct amii_WinDesc *cw;
    register int win;
    register dlb *fp;
    register char *t;
    register char buf[ 200 ];

    if( fn == NULL )
	panic("NULL file name in display_file()");

    if( ( fp = dlb_fopen( fn, RDTMODE ) ) == (dlb *)NULL )
    {
	if (complain) {
	    sprintf( buf, "Can't display %s: %s", fn,
#if defined(_DCC) || defined(__GNUC__)
			strerror(errno)
#else
# ifdef  __SASC_60
			__sys_errlist[ errno ]
# else
			sys_errlist[ errno ]
# endif
#endif
			);
	    amii_addtopl( buf );
	}
	return;
    }
    win = amii_create_nhwindow( NHW_TEXT );

    /* Set window title to file name */
    if( cw = amii_wins[ win ] )
	cw->morestr = (char *)fn;

    while( dlb_fgets( buf, sizeof( buf ), fp ) != NULL )
    {
	if( t = index( buf, '\n' ) )
	    *t = 0;
	amii_putstr( win, 0, buf );
    }
    dlb_fclose( fp );

    /* If there were lines in the file, display those lines */

    if( amii_wins[ win ]->cury > 0 )
	amii_display_nhwindow( win, TRUE );

    amii_wins[win]->morestr = NULL;		/* don't free title string */
    amii_destroy_nhwindow( win );
}

/* Put a 3-D motif border around the gadget.  String gadgets or those
 * which do not have highlighting are rendered down.  Boolean gadgets
 * are rendered in the up position by default.
 */

void
SetBorder( gd )
    register struct Gadget *gd;
{
    register struct Border *bp;
    register short *sp;
    register int i, inc = -1, dec = -1;
    int borders = 6;
    int hipen = flags.amii_dripens[ SHINEPEN ], shadowpen = flags.amii_dripens[ SHADOWPEN ];
#ifdef	INTUI_NEW_LOOK
    struct DrawInfo *dip;
#endif

#ifdef	INTUI_NEW_LOOK
    if( IntuitionBase->LibNode.lib_Version >= 37 )
    {
	if( dip = GetScreenDrawInfo( HackScreen ) )
	{
	    hipen = dip->dri_Pens[ SHINEPEN ];
	    shadowpen = dip->dri_Pens[ SHADOWPEN ];
	    FreeScreenDrawInfo( HackScreen, dip );
	}
    }
#endif
    /* Allocate two border structures one for up image and one for down
     * image, plus vector arrays for the border lines.
     */

    if( gd->GadgetType == STRGADGET )
	borders = 12;

    if( ( bp = (struct Border *)alloc( ( ( sizeof( struct Border ) * 2 ) +
			( sizeof( short ) * borders ) ) * 2 ) ) == NULL )
    {
	return;
    }

    /* For a string gadget, we expand the border beyond the area where
     * the text will be entered.
     */

    /* Remove any special rendering flags to avoid confusing intuition
     */

    gd->Flags &= ~(GADGHIGHBITS|GADGIMAGE);

    sp = (short *)(bp + 4);
    if( gd->GadgetType == STRGADGET || ( gd->GadgetType == BOOLGADGET &&
			    ( gd->Flags & GADGHIGHBITS ) == GADGHNONE ) )
    {
	sp[0] = -1;
	sp[1] = gd->Height - 1;
	sp[2] = -1;
	sp[3] = -1;
	sp[4] = gd->Width - 1;
	sp[5] = -1;

	sp[6] = gd->Width + 1;
	sp[7] = -2;
	sp[8] = gd->Width + 1;
	sp[9] = gd->Height + 1;
	sp[10] = -2;
	sp[11] = gd->Height + 1;

	sp[12] = -2;
	sp[13] = gd->Height;
	sp[14] = -2;
	sp[15] = -2;
	sp[16] = gd->Width;
	sp[17] = -2;
	sp[18] = gd->Width;
	sp[19] = gd->Height;
	sp[20] = -2;
	sp[21] = gd->Height;

	for( i = 0; i < 3; ++i )
	{
	    bp[ i ].LeftEdge = bp[ i ].TopEdge = -1;
	    bp[ i ].FrontPen = ( i == 0 || i == 1 ) ? shadowpen : hipen;

	    /* Have to use JAM2 so that the old colors disappear. */
	    bp[ i ].BackPen = C_BLACK;
	    bp[ i ].DrawMode = JAM2;
	    bp[ i ].Count = ( i == 0 || i == 1 ) ? 3 : 5;
	    bp[ i ].XY = &sp[ i*6 ];
	    bp[ i ].NextBorder = ( i == 2 ) ? NULL : &bp[ i + 1 ];
	}

	/* bp[0] and bp[1] two pieces for the up image */
	gd->GadgetRender = (APTR) bp;

	/* No image change for select */
	gd->SelectRender = (APTR) bp;

	gd->LeftEdge++;
	gd->TopEdge++;
	gd->Flags |= GADGHCOMP;
    }
    else
    {
	/* Create the border vector values for up and left side, and
	 * also the lower and right side.
	 */

	sp[0] = dec;
	sp[1] = gd->Height + inc;
	sp[2] = dec;
	sp[3] = dec;
	sp[4] = gd->Width + inc;
	sp[5] = dec;

	sp[6] = gd->Width + inc;
	sp[7] = dec;
	sp[8] = gd->Width + inc;
	sp[9] = gd->Height + inc;
	sp[10] = dec;
	sp[11] = gd->Height + inc;

	/* We are creating 4 sets of borders, the two sides of the
	 * rectangle share the border vectors with the opposite image,
	 * but specify different colors.
	 */

	for( i = 0; i < 4; ++i )
	{
	    bp[ i ].TopEdge = bp[ i ].LeftEdge = 0;

	    /* A GADGHNONE is always down */

	    if( gd->GadgetType == BOOLGADGET &&
			    ( gd->Flags & GADGHIGHBITS ) != GADGHNONE )
	    {
		bp[ i ].FrontPen =
			    ( i == 1 || i == 2 ) ? shadowpen : hipen;
	    }
	    else
	    {
		bp[ i ].FrontPen =
			    ( i == 1 || i == 3 ) ? hipen : shadowpen;
	    }

	    /* Have to use JAM2 so that the old colors disappear. */
	    bp[ i ].BackPen = C_BLACK;
	    bp[ i ].DrawMode = JAM2;
	    bp[ i ].Count = 3;
	    bp[ i ].XY = &sp[ 6 * ((i &1) != 0) ];
	    bp[ i ].NextBorder =
			    ( i == 1 || i == 3 ) ? NULL : &bp[ i + 1 ];
	}

	/* bp[0] and bp[1] two pieces for the up image */
	gd->GadgetRender = (APTR) bp;

	/* bp[2] and bp[3] two pieces for the down image */
	gd->SelectRender = (APTR) (bp + 2);
	gd->Flags |= GADGHIMAGE;
    }
}

/* Following function copied from wintty.c */
/* Modified slightly to fit amiga needs */

void
amii_player_selection()
{
	int i, k, n;
	char pick4u = 'n', thisch, lastch = 0;
	char pbuf[QBUFSZ], plbuf[QBUFSZ], rolenamebuf[QBUFSZ];
	winid win;
	anything any;
	menu_item *selected = 0;

	rigid_role_checks();

	/* Should we randomly pick for the player? */
	if (flags.initrole == ROLE_NONE || flags.initrace == ROLE_NONE ||
		flags.initgend == ROLE_NONE || flags.initalign == ROLE_NONE) {
	    char *prompt = build_plselection_prompt(pbuf, QBUFSZ, flags.initrole,
				flags.initrace, flags.initgend, flags.initalign);
	    pline("%s", prompt);
	    do {	/* loop until we get valid input */
		cursor_on(WIN_MESSAGE);
		pick4u = lowc(WindowGetchar());
		cursor_off(WIN_MESSAGE);
		if (index(quitchars, pick4u)) pick4u = 'y';
	    } while(!index(ynqchars, pick4u));
	    pbuf[0] = pick4u;
	    pbuf[1] = 0;
	    amii_addtopl(pbuf);

	    if (pick4u != 'y' && pick4u != 'n') {
give_up:	/* Quit */
		if (selected) free((genericptr_t) selected);
		clearlocks();
		exit_nhwindows(NULL);
		terminate(0);
		/*NOTREACHED*/
		return;
	    }
	}

	(void) root_plselection_prompt(plbuf, QBUFSZ - 1,
			flags.initrole, flags.initrace, flags.initgend, flags.initalign);

	/* Select a role, if necessary */
	/* we'll try to be compatible with pre-selected race/gender/alignment,
	 * but may not succeed */
	if (flags.initrole < 0) {
	    /* Process the choice */
	    if (pick4u == 'y' || flags.initrole == ROLE_RANDOM || flags.randomall) {
		/* Pick a random role */
		flags.initrole = pick_role(flags.initrace, flags.initgend,
						flags.initalign, PICK_RANDOM);
		if (flags.initrole < 0) {
		    amii_putstr(WIN_MESSAGE, 0, "Incompatible role!");
		    flags.initrole = randrole();
		}
	    } else {
		/* Prompt for a role */
		win = create_nhwindow(NHW_MENU);
		start_menu(win);
		any.a_void = 0;         /* zero out all bits */
		for (i = 0; roles[i].name.m; i++) {
		    if (ok_role(i, flags.initrace, flags.initgend,
							flags.initalign)) {
			any.a_int = i+1;	/* must be non-zero */
			thisch = lowc(roles[i].name.m[0]);
			if (thisch == lastch) thisch = highc(thisch);
			if (flags.initgend != ROLE_NONE && flags.initgend != ROLE_RANDOM) {
				if (flags.initgend == 1  && roles[i].name.f)
					Strcpy(rolenamebuf, roles[i].name.f);
				else
					Strcpy(rolenamebuf, roles[i].name.m);
			} else {
				if (roles[i].name.f) {
					Strcpy(rolenamebuf, roles[i].name.m);
					Strcat(rolenamebuf, "/");
					Strcat(rolenamebuf, roles[i].name.f);
				} else 
					Strcpy(rolenamebuf, roles[i].name.m);
			}	
			add_menu(win, NO_GLYPH, &any, thisch,
			    0, ATR_NONE, an(rolenamebuf), MENU_UNSELECTED);
			lastch = thisch;
		    }
		}
		any.a_int = pick_role(flags.initrace, flags.initgend,
				    flags.initalign, PICK_RANDOM)+1;
		if (any.a_int == 0)	/* must be non-zero */
		    any.a_int = randrole()+1;
		add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
				"Random", MENU_UNSELECTED);
		any.a_int = i+1;	/* must be non-zero */
		add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
				"Quit", MENU_UNSELECTED);
		Sprintf(pbuf, "Pick a role for your %s", plbuf);
		end_menu(win, pbuf);
		n = select_menu(win, PICK_ONE, &selected);
		destroy_nhwindow(win);

		/* Process the choice */
		if (n != 1 || selected[0].item.a_int == any.a_int)
		    goto give_up;		/* Selected quit */

		flags.initrole = selected[0].item.a_int - 1;
		free((genericptr_t) selected),	selected = 0;
	    }
	    (void) root_plselection_prompt(plbuf, QBUFSZ - 1,
			flags.initrole, flags.initrace, flags.initgend, flags.initalign);
	}

	/* Select a race, if necessary */
	/* force compatibility with role, try for compatibility with
	 * pre-selected gender/alignment */
	if (flags.initrace < 0 || !validrace(flags.initrole, flags.initrace)) {
	    /* pre-selected race not valid */
	    if (pick4u == 'y' || flags.initrace == ROLE_RANDOM || flags.randomall) {
		flags.initrace = pick_race(flags.initrole, flags.initgend,
							flags.initalign, PICK_RANDOM);
		if (flags.initrace < 0) {
		    amii_putstr(WIN_MESSAGE, 0, "Incompatible race!");
		    flags.initrace = randrace(flags.initrole);
		}
	    } else {	/* pick4u == 'n' */
		/* Count the number of valid races */
		n = 0;	/* number valid */
		k = 0;	/* valid race */
		for (i = 0; races[i].noun; i++) {
		    if (ok_race(flags.initrole, i, flags.initgend,
							flags.initalign)) {
			n++;
			k = i;
		    }
		}
		if (n == 0) {
		    for (i = 0; races[i].noun; i++) {
			if (validrace(flags.initrole, i)) {
			    n++;
			    k = i;
			}
		    }
		}

		/* Permit the user to pick, if there is more than one */
		if (n > 1) {
		    win = create_nhwindow(NHW_MENU);
		    start_menu(win);
		    any.a_void = 0;         /* zero out all bits */
		    for (i = 0; races[i].noun; i++)
			if (ok_race(flags.initrole, i, flags.initgend,
							flags.initalign)) {
			    any.a_int = i+1;	/* must be non-zero */
			    add_menu(win, NO_GLYPH, &any, races[i].noun[0],
				0, ATR_NONE, races[i].noun, MENU_UNSELECTED);
			}
		    any.a_int = pick_race(flags.initrole, flags.initgend,
					flags.initalign, PICK_RANDOM)+1;
		    if (any.a_int == 0)	/* must be non-zero */
			any.a_int = randrace(flags.initrole)+1;
		    add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
				    "Random", MENU_UNSELECTED);
		    any.a_int = i+1;	/* must be non-zero */
		    add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
				    "Quit", MENU_UNSELECTED);
		    Sprintf(pbuf, "Pick the race of your %s", plbuf);
		    end_menu(win, pbuf);
		    n = select_menu(win, PICK_ONE, &selected);
		    destroy_nhwindow(win);
		    if (n != 1 || selected[0].item.a_int == any.a_int)
			goto give_up;		/* Selected quit */

		    k = selected[0].item.a_int - 1;
		    free((genericptr_t) selected),	selected = 0;
		}
		flags.initrace = k;
	    }
	    (void) root_plselection_prompt(plbuf, QBUFSZ - 1,
			flags.initrole, flags.initrace, flags.initgend, flags.initalign);
	}

	/* Select a gender, if necessary */
	/* force compatibility with role/race, try for compatibility with
	 * pre-selected alignment */
	if (flags.initgend < 0 || !validgend(flags.initrole, flags.initrace,
						flags.initgend)) {
	    /* pre-selected gender not valid */
	    if (pick4u == 'y' || flags.initgend == ROLE_RANDOM || flags.randomall) {
		flags.initgend = pick_gend(flags.initrole, flags.initrace,
						flags.initalign, PICK_RANDOM);
		if (flags.initgend < 0) {
		    amii_putstr(WIN_MESSAGE, 0, "Incompatible gender!");
		    flags.initgend = randgend(flags.initrole, flags.initrace);
		}
	    } else {	/* pick4u == 'n' */
		/* Count the number of valid genders */
		n = 0;	/* number valid */
		k = 0;	/* valid gender */
		for (i = 0; i < ROLE_GENDERS; i++) {
		    if (ok_gend(flags.initrole, flags.initrace, i,
							flags.initalign)) {
			n++;
			k = i;
		    }
		}
		if (n == 0) {
		    for (i = 0; i < ROLE_GENDERS; i++) {
			if (validgend(flags.initrole, flags.initrace, i)) {
			    n++;
			    k = i;
			}
		    }
		}

		/* Permit the user to pick, if there is more than one */
		if (n > 1) {
		    win = create_nhwindow(NHW_MENU);
		    start_menu(win);
		    any.a_void = 0;         /* zero out all bits */
		    for (i = 0; i < ROLE_GENDERS; i++)
			if (ok_gend(flags.initrole, flags.initrace, i,
							    flags.initalign)) {
			    any.a_int = i+1;
			    add_menu(win, NO_GLYPH, &any, genders[i].adj[0],
				0, ATR_NONE, genders[i].adj, MENU_UNSELECTED);
			}
		    any.a_int = pick_gend(flags.initrole, flags.initrace,
					    flags.initalign, PICK_RANDOM)+1;
		    if (any.a_int == 0)	/* must be non-zero */
			any.a_int = randgend(flags.initrole, flags.initrace)+1;
		    add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
				    "Random", MENU_UNSELECTED);
		    any.a_int = i+1;	/* must be non-zero */
		    add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
				    "Quit", MENU_UNSELECTED);
		    Sprintf(pbuf, "Pick the gender of your %s", plbuf);
		    end_menu(win, pbuf);
		    n = select_menu(win, PICK_ONE, &selected);
		    destroy_nhwindow(win);
		    if (n != 1 || selected[0].item.a_int == any.a_int)
			goto give_up;		/* Selected quit */

		    k = selected[0].item.a_int - 1;
		    free((genericptr_t) selected),	selected = 0;
		}
		flags.initgend = k;
	    }
	    (void)  root_plselection_prompt(plbuf, QBUFSZ - 1,
			flags.initrole, flags.initrace, flags.initgend, flags.initalign);
	}

	/* Select an alignment, if necessary */
	/* force compatibility with role/race/gender */
	if (flags.initalign < 0 || !validalign(flags.initrole, flags.initrace,
							flags.initalign)) {
	    /* pre-selected alignment not valid */
	    if (pick4u == 'y' || flags.initalign == ROLE_RANDOM || flags.randomall) {
		flags.initalign = pick_align(flags.initrole, flags.initrace,
							flags.initgend, PICK_RANDOM);
		if (flags.initalign < 0) {
		    amii_putstr(WIN_MESSAGE, 0, "Incompatible alignment!");
		    flags.initalign = randalign(flags.initrole, flags.initrace);
		}
	    } else {	/* pick4u == 'n' */
		/* Count the number of valid alignments */
		n = 0;	/* number valid */
		k = 0;	/* valid alignment */
		for (i = 0; i < ROLE_ALIGNS; i++) {
		    if (ok_align(flags.initrole, flags.initrace, flags.initgend,
							i)) {
			n++;
			k = i;
		    }
		}
		if (n == 0) {
		    for (i = 0; i < ROLE_ALIGNS; i++) {
			if (validalign(flags.initrole, flags.initrace, i)) {
			    n++;
			    k = i;
			}
		    }
		}

		/* Permit the user to pick, if there is more than one */
		if (n > 1) {
		    win = create_nhwindow(NHW_MENU);
		    start_menu(win);
		    any.a_void = 0;         /* zero out all bits */
		    for (i = 0; i < ROLE_ALIGNS; i++)
			if (ok_align(flags.initrole, flags.initrace,
							flags.initgend, i)) {
			    any.a_int = i+1;
			    add_menu(win, NO_GLYPH, &any, aligns[i].adj[0],
				 0, ATR_NONE, aligns[i].adj, MENU_UNSELECTED);
			}
		    any.a_int = pick_align(flags.initrole, flags.initrace,
					    flags.initgend, PICK_RANDOM)+1;
		    if (any.a_int == 0)	/* must be non-zero */
			any.a_int = randalign(flags.initrole, flags.initrace)+1;
		    add_menu(win, NO_GLYPH, &any , '*', 0, ATR_NONE,
				    "Random", MENU_UNSELECTED);
		    any.a_int = i+1;	/* must be non-zero */
		    add_menu(win, NO_GLYPH, &any , 'q', 0, ATR_NONE,
				    "Quit", MENU_UNSELECTED);
		    Sprintf(pbuf, "Pick the alignment of your %s", plbuf);
		    end_menu(win, pbuf);
		    n = select_menu(win, PICK_ONE, &selected);
		    destroy_nhwindow(win);
		    if (n != 1 || selected[0].item.a_int == any.a_int)
			goto give_up;		/* Selected quit */

		    k = selected[0].item.a_int - 1;
		    free((genericptr_t) selected),	selected = 0;
		}
		flags.initalign = k;
	    }
	}
	/* Success! */
}
#endif /* AMIGA_INTUITION */
